package com.einmeer.sercuritydemo.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;

import static org.springframework.security.config.Customizer.withDefaults;

/**
 * @author 芊嵛
 * @date 2024/3/1
 */

@Configuration //配置类
@EnableMethodSecurity // 授权方式三：开启基于方法授权
//@EnableWebSecurity //开启SpringSecurity的自定义配置（在SpringBoot项目中可以省略此注解）
public class WebSecurityConfig {
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        //authorizeRequests()：开启授权保护
        http.authorizeRequests(authorize -> authorize
                // 授权方式一：用户-权限-资源的方法
//                .requestMatchers("/user/list").hasAnyAuthority("USER_LIST")
//                .requestMatchers("/user/add").hasAnyAuthority("USER_LIST")


                // 授权方式二：用户-角色-资源的方法
//                .requestMatchers("/user/**").hasRole("ADMIN")



                //anyRequest()：对所有请求开启授权保护
                .anyRequest()
                //authenticated()：已认证请求会自动被授权
                .authenticated())
//                .formLogin(withDefaults())  // 表单授权方式(默认方式)
        ;

        // 登录配置方式一(建议)
        http.formLogin(form -> {
            form.loginPage("/login")
                    .permitAll() // 无需授权即可访问
                    .usernameParameter("myUsername") // 修改传入的用户名字，默认username
                    .passwordParameter("myPassword") // 修改传入的用户密码，默认password
//                            .failureUrl("/login?error") // 校验失败时跳转的地址，默认是error
                    .successHandler(new MyAuthenticationSuccessHandler()) // 认证成功时的处理
                    .failureHandler(new MyAuthenticationFailureHandler()) // 认证失败处理
            ;
        });
        // 登录配置方式二(不建议)
//        http.httpBasic(withDefaults());  // 基本授权方式(浏览器自带的，没有登出，不建议用)


        // 注销配置
        http.logout(logout -> {
            logout.logoutSuccessHandler(new MyLogoutSuccessHandler());
        });

        // 异常配置
        http.exceptionHandling(exception -> {
            exception.authenticationEntryPoint(new MyAuthenticationEntryPoint()); // 请求未认证处理
            exception.accessDeniedHandler(new MyAccessDeniedHandler()); // 没有权限
        });

        // 会话下面的设置只允许同一浏览器登录，两个就会被顶掉
        http.sessionManagement(session -> {
            session.maximumSessions(1).expiredSessionStrategy(new MySessionInformationExpiredStrategy());
        });

        // 跨域
        http.csrf(withDefaults());

        // 这里用来关闭csrf跨站请求伪造，有前端的话在前端配置就不用关闭了(如果先后端不分离那就在写了表单就能开启)
        http.csrf(csrf -> csrf.disable()); // 注释后是开启关闭

        return http.build();
    }


    /**
     * 创建基于内存的用户信息管理器，设定好了账号和密码
     * @return
     */
//    @Bean
//    public UserDetailsService userDetailsService() {
//        // 创建基于内存的用户信息管理器
//        InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
//        // 使用manager管理UserDetails对象
//        manager.createUser(
//                // 创建UserDetails对象，用于管理用户名、用户密码、用户角色用户权限等功能
//                //
//                User.withDefaultPasswordEncoder().username("user").password("password").roles("USER").build()
//        );
//        return manager;
//    }

    /**
     * 创建基于数据库的用户信息管理器
     * 因为只有这一个对象要被创建在上下文中所以可以不在这里写,在DBUSerDetailsManager中加@Component就行
     * 二者只能留一个
     * @return
     */
//    @Bean
//    public UserDetailsService userDetailsService() {
//        // 创建基于数据库的用户信息管理器
//        DBUserDetailsManager manager = new DBUserDetailsManager();
//        return manager;
//    }
}